package sf.member.controller;

import java.util.Date;
import java.util.Optional;
import java.util.concurrent.TimeUnit;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.lang.RandomStringUtils;
import org.joda.time.DateTime;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;

import com.nimbusds.jose.JOSEException;

import sf.core.controller.BaseController;
import sf.core.entity.TuMember;
import sf.core.exception.SugarException;
import sf.core.mapper.TuMemberMapper;
import sf.core.service.AuthService;
import sf.core.service.KeyValueService;
import sf.core.service.SmsService;
import sf.member.utils.CryptUtil;
import sf.member.vo.AuthToken;

@RequestMapping("/auth")
@ResponseBody
@Controller
public class AuthController extends BaseController {

	private final static int ALIVE_TIME_IN_SECOND = 1800;
	private final static int BAN_TIME_IN_SECOND = 60;
	private final static int ALIVE_AUTH_TOKEN_IN_DAY = 14;

	@Resource
	TuMemberMapper memberMapper;

	@Resource
	SmsService smsService;

	@Resource
	KeyValueService keyValueService;

	@Resource
	AuthService<Integer> authService;

	@RequestMapping(value = "/passcode")
	public void genPasscode(@RequestParam Long mobile) throws SugarException {
		String strMobile = Long.toString(mobile);
		if (keyValueService.hasKey(strMobile)
				&& (ALIVE_TIME_IN_SECOND - keyValueService.ttl(strMobile)) < BAN_TIME_IN_SECOND) {
			throw new SugarException("send message too frequently");
		} else {
			String code = RandomStringUtils.randomNumeric(6);
			keyValueService.set(strMobile, code, ALIVE_TIME_IN_SECOND, TimeUnit.SECONDS);
			smsService.sendVerificationCode(strMobile, code);
		}
	}

	@RequestMapping(value = "/signup", method = RequestMethod.POST)
	public AuthToken signup(@RequestParam Long mobile, @RequestParam String passcode)
			throws SugarException, JOSEException {

		String strMobile = Long.toString(mobile);
		if (keyValueService.hasKey(strMobile) && keyValueService.get(strMobile).equals(passcode)) {
			TuMember cond = new TuMember();
			cond.setMobile(mobile);

			Optional<TuMember> opt = Optional.ofNullable(memberMapper.selectOne(cond));
			Integer uid;
			if (opt.isPresent()) {
				uid = opt.get().getId();
			} else {
				TuMember inst = new TuMember();
				inst.setMobile(mobile);
				memberMapper.insert(inst);
				uid = inst.getId();
			}
			return createToken(uid);

		} else {
			throw new SugarException("Wrong passcode");
		}

	}

	@RequestMapping(value = "/login", method = RequestMethod.POST)
	public AuthToken login(@RequestParam Long mobile, @RequestParam String password)
			throws SugarException, JOSEException {

		TuMember cond = new TuMember();
		String strMobile = Long.toString(mobile);
		cond.setPassword(CryptUtil.hash(password, strMobile));
		Optional<TuMember> found = Optional.ofNullable(memberMapper.selectOne(cond));
		if (found.isPresent()) {
			return createToken(found.get().getId());
		} else {
			throw new SugarException(HttpServletResponse.SC_UNAUTHORIZED, "login failed");
		}
	}

	private AuthToken createToken(int uid) throws JOSEException {
		Date expire = DateTime.now().plusDays(ALIVE_AUTH_TOKEN_IN_DAY).toDate();
		return new AuthToken(authService.createToken(uid, DateTime.now().toDate(), expire), expire.getTime());
	}

}
